import cv2
import numpy as np
import pyautogui
import time
import os

def screen_capture():
    # 使用pyautogui截取当前屏幕
    screenshot = pyautogui.screenshot()


    # 将截取的图片转换为opencv格式
    screen_cv = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR)

    # 只需要左下角图片，但这样点击按钮坐标要重算，麻烦
    # screen_cv = crop_left_bottom_half(screen_cv)
    return screen_cv


def crop_left_bottom_half(image):
    # 获取图像的高度和宽度
    height, width = image.shape[:2]
    
    # 计算新的宽度和高度
    new_width = width // 2
    new_height = height // 2
    
    # 裁剪左下角一半的区域
    # OpenCV的图像数组是先高度后宽度
    cropped_image = image[new_height:, 0:new_width]
    
    return cropped_image


def multi_scale_template_matching(main_binary, template_file, scale_range, threshold):
    template = cv2.imread(template_file)
    # 将图像转换为灰度图
    template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)

    # 应用阈值处理进行二值化
    _, template_binary = cv2.threshold(template_gray, 240, 255, cv2.THRESH_BINARY)

    # 初始化匹配结果
    best_match = None
    best_scale = 0
    best_value = 0

    main_h, main_w = main_binary.shape

    # 遍历缩放范围内的每个缩放因子
    for scale in scale_range:
        # 缩放模板图像
        scaled_template = cv2.resize(template_binary, (0, 0), fx=scale, fy=scale)
        scaled_h, scaled_w = scaled_template.shape

        # 如果缩放后的模板尺寸大于主图像的尺寸，则跳过这个尺度
        if scaled_w > main_w or scaled_h > main_h:
            continue

        # 执行模板匹配
        match_result = cv2.matchTemplate(main_binary, scaled_template, cv2.TM_CCOEFF_NORMED)

        # 找出最大匹配值
        _, max_value, _, max_loc = cv2.minMaxLoc(match_result)

        # 如果新的匹配值更高，并且超过阈值，则更新最佳匹配结果
        if max_value > threshold and max_value > best_value:
            best_match = max_loc
            best_scale = scale
            best_value = max_value
    
    # 如果找到了匹配模板
    if best_match:
        # print(f'Template {template_file} found at scale: {best_scale} with match value: {best_value}')
        
        # 绘制矩形框标出匹配位置
        # top_left = best_match
        # scaled_template = cv2.resize(template_binary, (0, 0), fx=best_scale, fy=best_scale)
        # scaled_h, scaled_w = scaled_template.shape
        # bottom_right = (top_left[0] + scaled_w, top_left[1] + scaled_h)
        # cv2.rectangle(main_binary, top_left, bottom_right, (0, 255, 0), 2)

        # 显示结果
        # cv2.imshow('Detected', main_binary)
        # cv2.waitKey(0)
        # cv2.destroyAllWindows()
        return template_file.replace('.png', '').split('/')[-1]
    else:
        # print('Template not found!')
        return None




def multi_scale_button_matching(main_gray, template_file, scale_range, threshold):
    template = cv2.imread(template_file)
    # 将图像转换为灰度图
    template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)

    # 初始化匹配结果
    best_match = None
    best_scale = 0
    best_value = 0

    main_h, main_w = template_gray.shape

    # 遍历缩放范围内的每个缩放因子
    for scale in scale_range:
        # 缩放模板图像
        scaled_template = cv2.resize(template_gray, (0, 0), fx=scale, fy=scale)
        scaled_h, scaled_w = scaled_template.shape

        # 如果缩放后的模板尺寸大于主图像的尺寸，则跳过这个尺度
        if scaled_w > main_w or scaled_h > main_h:
            continue

        # 执行模板匹配
        match_result = cv2.matchTemplate(main_gray, scaled_template, cv2.TM_CCOEFF_NORMED)

        # 找出最大匹配值
        _, max_value, _, max_loc = cv2.minMaxLoc(match_result)

        # 如果新的匹配值更高，并且超过阈值，则更新最佳匹配结果
        if max_value > threshold and max_value > best_value:
            best_match = max_loc
            best_scale = scale
            best_value = max_value
    
    # 如果找到了匹配模板
    if best_match:
        # print(f'Template {template_file} found at scale: {best_scale} with match value: {best_value}')
        
        # 绘制矩形框标出匹配位置
        # top_left = best_match
        # scaled_template = cv2.resize(template_gray, (0, 0), fx=best_scale, fy=best_scale)
        # scaled_h, scaled_w = scaled_template.shape
        # bottom_right = (top_left[0] + scaled_w, top_left[1] + scaled_h)
        # cv2.rectangle(main_gray, top_left, bottom_right, (0, 255, 0), 2)

        # 显示结果
        # cv2.imshow('Detected', main_gray)
        # cv2.waitKey(0)
        # cv2.destroyAllWindows()
        return best_match
    else:
        # print('Template not found!')
        return None
        

def has_check(image_gray):
    check_btn_file = 'templates/op/check.png'
    scale_range = np.linspace(0.5, 2.0, 10)
    threshold = 0.8
    best_match = multi_scale_button_matching(image_gray, check_btn_file, scale_range, threshold)
    if best_match:
        # 校准截屏图像和屏幕的坐标差
        width, height = pyautogui.size()
        i_height, i_width = image_gray.shape[:2]
        x, y = best_match
        x = x / i_width * width + 8
        y = y / i_height * height + 8
        return x, y
    return None


def has_fold(image_gray):
    check_btn_file = 'templates/op/fold.png'
    scale_range = np.linspace(0.5, 2.0, 10)
    threshold = 0.8
    best_match = multi_scale_button_matching(image_gray, check_btn_file, scale_range, threshold)
    if best_match:
        # 校准截屏图像和屏幕的坐标差
        width, height = pyautogui.size()
        i_height, i_width = image_gray.shape[:2]
        x, y = best_match
        x = x / i_width * width + 8
        y = y / i_height * height + 8
        return x, y
    return None


def check_dealer(image_gray):
    check_btn_file = 'templates/op/dealer.png'
    scale_range = np.linspace(0.5, 2.0, 10)
    threshold = 0.8
    best_match = multi_scale_button_matching(image_gray, check_btn_file, scale_range, threshold)
    if best_match:
        # 校准截屏图像和屏幕的坐标差
        width, height = pyautogui.size()
        i_height, i_width = image_gray.shape[:2]
        x, y = best_match
        x = x / i_width * width
        y = y / i_height * height
        if y > height * 0.8:
            return True
    return False


def find_numbers(image):
    # 转为灰度图像
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # 应用阈值化显示清晰的数字轮廓
    thresh, img = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY)
    # thresh, img = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY_INV)

    # 查找轮廓
    contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    first_card_color = None
    first_card_number = None

    scale_range = np.linspace(0.5, 2.0, 20)  # 缩放比例为0.5到2.0

    # 检测是否是翻牌前
    poker_cnt = 0
    for cnt in contours:
        # 获取轮廓的边界框，x，y是矩阵左上点的坐标，w，h是矩阵的宽和高
        x, y, w, h = cv2.boundingRect(cnt)
        if h / w < 1.4 or h / w > 1.5 or h < 100:
            continue
        poker_cnt += 1

    if poker_cnt > 2:
        # 大于两张牌，说明是翻后，不进行检测和点击
        return

    if check_dealer(gray):
        print('dealer')
        os.system("""
        osascript -e 'display notification " Dealer" with title "Good"'
        """)
        os.system('say "Dealer"')
        time.sleep(20)
        return


    # 有过牌按钮则过牌并提示
    # 否则弃牌按钮情况下，检测是否是好牌
    check = has_check(gray)
    if check is not None:
        pyautogui.moveTo(check, duration=0.2)
        pyautogui.click()
        pyautogui.click()
        # 存在过牌按钮，说明翻前大盲位，不用检测，直接提示
        print('Directly check')
        os.system("""
        osascript -e 'display notification " Directly check" with title "Good"'
        """)
        os.system('say "Directly check"')
        return

    for cnt in contours:
        # 获取轮廓的边界框，x，y是矩阵左上点的坐标，w，h是矩阵的宽和高
        x, y, w, h = cv2.boundingRect(cnt)

        # 根据窗口大小可能需要调整
        if h / w < 1.4 or h / w > 1.5 or h < 100:
            continue

        # 防止手牌暗色，误测到翻牌
        # 校准截屏图像和屏幕的坐标差
        width, height = pyautogui.size()
        i_height, i_width = gray.shape[:2]
        tmp_y = y / i_height * height + 8
        if tmp_y < height * 0.7:
            continue

        # 在原图上绘制边界框
        # cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
        # 裁切扑克区域
        roi = img[y:y+h, x:x+w]
        # cv2.imshow('test', roi)
        # cv2.waitKey(0)
        # cv2.destroyAllWindows()

        # 读取所有扑克牌花色模板
        template_dir = 'templates/color/'
        template_files = [f for f in os.listdir(template_dir) if f.endswith('.png')]
        threshold = 0.91  # 针对花色判断
        m_color = None
        m_number = None
        for tfile in template_files:
            m_color = multi_scale_template_matching(roi, template_dir + tfile, scale_range, threshold)
            if m_color is not None:
                # print(m_color)
                break

        if m_color is None:
            threshold = 0.85
            for tfile in template_files:
                m_color = multi_scale_template_matching(roi, template_dir + tfile, scale_range, threshold)
                if m_color is not None:
                    # print(m_color)
                    # 找到跳出
                    break


        # 读取所有扑克牌数字字母模板
        template_dir = 'templates/number/'
        template_files = [f for f in os.listdir(template_dir) if f.endswith('.png')]
        threshold = 0.8  # 针对数字字母判断
        for tfile in template_files:
            m_number = multi_scale_template_matching(roi, template_dir + tfile, scale_range, threshold)
            if m_number is not None:
                # print('debug', m_number)
                # 找到跳出
                break

        if m_number is None:
            threshold = 0.7
            for tfile in template_files:
                m_number = multi_scale_template_matching(roi, template_dir + tfile, scale_range, threshold)
                if m_number is not None:
                    # print('debug', m_number)
                    # 找到跳出
                    break


        if m_number is None:
            print('alert m_number is None!!!')
            os.system("""
            osascript -e 'display notification " number is None" with title "Failure"'
            """)
            os.system('say "number is None"')
            continue

        if m_number == 'T':
            m_number = 10
        elif m_number == 'J':
            m_number = 11
        elif m_number == 'Q':
            m_number = 12
        elif m_number == 'K':
            m_number = 13
        elif m_number == 'A':
            m_number = 14
        else:
            m_number = int(m_number)


        # 提醒策略定制
        if first_card_number is None:
            first_card_color = m_color
            first_card_number = m_number
        else:
            good = False
            # 对子
            if m_number == first_card_number:
                good = True
            # 连张同花和卡张
            elif abs(m_number - first_card_number) < 3 and first_card_color == m_color:
                good = True
            # 两高牌
            elif first_card_number > 9 and m_number > 9:
                good = True
            # A花
            elif first_card_color == m_color and (first_card_number == 14 or m_number == 14):
                good = True
            # 中大连张
            elif (first_card_number > 7 or m_number > 7) and abs(m_number - first_card_number) < 2:
                good = True

            if good == True:
                print(f'Good, {first_card_color}, {first_card_number}, {m_color}, {m_number}')
                os.system("""
                osascript -e 'display notification "{}, {}, {}, {}" with title "Good"'
                """.format(first_card_color, first_card_number, m_color, m_number))
                os.system('say' + f' "Good {first_card_number}, {m_number}"')
                time.sleep(20)
            else:
                fold = has_fold(gray)
                if fold is not None:
                    pyautogui.moveTo(fold, duration=0.2)
                    pyautogui.click()
                    pyautogui.click()
                    # 存在过牌按钮，说明翻前大盲位，不用检测，直接提示
                    print(f'Directly fold, {first_card_color}, {first_card_number}, {m_color}, {m_number}')
                    return


if __name__ == '__main__':
    while True:
        img = screen_capture()
        find_numbers(img)
        time.sleep(2)
